package com.seckill.order.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seckill.framework.mybatisplus.WrapperParse;
import com.seckill.goods.feign.SkuFeign;
import com.seckill.goods.pojo.Sku;
import com.seckill.message.feign.MessageFeign;
import com.seckill.order.lock.RedisLock;
import com.seckill.order.lock.RedisLockImpl;
import com.seckill.order.mapper.OrderMapper;
import com.seckill.order.pojo.Order;
import com.seckill.order.service.OrderService;
import com.seckill.util.Result;
import com.seckill.util.ResultCode;
import com.seckill.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 作者
 * @since 2022-03-09
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MessageFeign messageFeign;

    @Autowired
    private RedissonClient redissonClient;
    /****
     * 热点商品下单
     * @param orderMap
     * @return
     */
    @Override
    public void hotAdd(Map<String, String> orderMap) throws IOException {
        //消息封装对象
        Map<String, Object> messageMap = new HashMap<String, Object>();

        String username = orderMap.get("username");
        String id = orderMap.get("id");

        //Redis中对应的key
        String key = "SKU_" + id;
        String lockkey = "LOCKSKU_" + id;
        String userKey = "USER" + username + "ID" + id;

        //如果key在redis缓存，则表示商品信息在Redis中进行操作
        boolean bo = true;// redissonDistributedLocker.tryLock(lockkey, 10, 10, TimeUnit.MINUTES);
        if (bo) {
            if (redisTemplate.hasKey(key)) {
                //获取商品数量
                Integer num = Integer.parseInt(redisTemplate.boundHashOps(key).get("num").toString());

                if (num <= 0) {
                    //商品售罄通知
                    messageMap.put("code", 20001);
                    messageMap.put("message", "商品已售罄");
                    messageFeign.send(username, JSON.toJSONString(messageMap));
                    return;
                }
                Result<Sku> skuResult = skuFeign.findById(id);
                Sku sku = skuResult.getData();

                //1.创建Order
                Order order = new Order();
                order.setTotalNum(1);
                order.setCreateTime(new Date());
                order.setUpdateTime(order.getCreateTime());
                order.setOrderStatus("0");
                order.setPayStatus("0");
                order.setConsignStatus("0");
                order.setSkuId(id);
                order.setName(sku.getName());
                order.setPrice(sku.getSeckillPrice() * order.getTotalNum());
                orderMapper.insert(order);

                //2.Redis中对应的num递减
                num--;
                if (num == 0) {
                    skuFeign.zero(id);
                }

                //2.清理用户排队信息
                Map<String, Object> allMap = new HashMap<String, Object>();
                allMap.put(userKey, 0);
                allMap.put("num", num);
                redisTemplate.boundHashOps(key).putAll(allMap);
                //3.记录用户购买过该商品,24小时后过期
                redisTemplate.boundValueOps(userKey).set("");
                redisTemplate.expire(userKey, 1, TimeUnit.MINUTES);

                //抢单成功通知
                messageMap.put("code", 200);
                messageMap.put("message", "抢单成功！");
                messageFeign.send(username, JSON.toJSONString(messageMap));
            }

            //释放锁
            //redissonDistributedLocker.unLock(lockkey);
            return;
        }

        //抢单失败通知
        messageMap.put("code", 20001);
        messageMap.put("message", "抢单失败！");
        messageFeign.send(username, JSON.toJSONString(messageMap));
    }

    /***
     * 添加订单
     * @param order
     * @return
     */
    @Override
    public int add(Order order) {
        String userKey = "USER" + order.getUsername() + "ID" + order.getSkuId();
        //1.递减库存
        Result<Sku> dcount = skuFeign.dcount(order.getSkuId(), order.getTotalNum());
        //2.递减成功->下单
        if (dcount.getCode() == ResultCode.DECOUNT_OK.getCode()) {
            Sku sku = dcount.getData();
            order.setOrderStatus("0");
            order.setPayStatus("0");
            order.setConsignStatus("0");
            order.setSkuId(sku.getId());
            order.setName(sku.getName());
            order.setPrice(sku.getSeckillPrice() * order.getTotalNum());
            orderMapper.insert(order);
            return ResultCode.ORDER_OK.getCode();
        }
        return dcount.getCode();
    }

    /**
     * Order条件+分页查询
     *
     * @param order 查询条件
     * @param page  页码
     * @param size  页大小
     * @return 分页结果
     */
    @Override
    public Page<Order> findPage(Order order, int page, int size) {
        //分页
        IPage<Order> iPage = new Page<Order>(page, size);
        //搜索条件构建
        QueryWrapper<Order> wrapper = WrapperParse.simple(order);

        // 排序
        wrapper.orderByDesc("create_time");

        //订单查询
        IPage<Order> pageInfo = orderMapper.selectPage(iPage, wrapper);
        List<Order> orders = pageInfo.getRecords();

        //查询每个订单的产品信息
        for (Order od : orders) {
            Result<Sku> skuResult = skuFeign.findById(od.getSkuId());
            if (skuResult.getData() != null) {
                od.setSku(skuResult.getData());
            }
        }
        //执行搜索
        return (Page<Order>) pageInfo;
    }

    @Override
    public Order findById(String id) {
        return orderMapper.selectById(id);
    }

    @Override
    public void pay(String id) {
        Order order = new Order();
        order.setId(id);
        order.setPayStatus("1");    //已支付
        orderMapper.updateById(order);

    }


    private volatile AtomicLong num = new AtomicLong(0);
    private volatile AtomicLong start = new AtomicLong(System.currentTimeMillis());

    @Autowired
    private RedisLock redisLock;
    /***
     * 下单操作
     * @param dataMap
     */
    @Async
    @Override
    public void saveOrder(Map<String, Object> dataMap) throws InterruptedException {
        //记录第一次下单时间
        if (num.incrementAndGet() == 1) {
            start.set(System.currentTimeMillis());
        }
        String id = dataMap.get("id").toString();
        String cat = dataMap.get("cat").toString();
        String username = dataMap.get("username").toString();

        if (StrUtil.hasEmpty(id, cat, username)) {
            throw new RuntimeException("下单参数异常!");
        }
        //分布式锁的时候到底要锁定什么资源,资源标识一定要出现在锁名称中
        //此处用LOCK + 商品分类 + 商品id
        String lockKey = "LOCK:" + cat + ":" + id;
        //下单之前加分布式锁 , 对商品数据加锁
        //基础实现,没有抽取
//        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, "1",30,TimeUnit.SECONDS);
//        if (!ifAbsent) {
//            return;
//        }

//        //最终方案: Redisson
//        RLock lock = redissonClient.getLock(lockKey);
//        boolean isLock = lock.tryLock();

        //boolean isLock = redisLock.tryLock(lockKey);
        boolean isLock = redisLock.tryLock(10L, TimeUnit.SECONDS, lockKey);
        if (!isLock) {
            return;
        }
        CountDownLatch countDownLatch = new CountDownLatch(1);
        RedisLockImpl redisLock = (RedisLockImpl) this.redisLock;
        redisLock.setCountDownLatch(countDownLatch);
        try {
            //Thread.sleep(2000);
            //2)获取商品价格、数量
            Object cn = redisTemplate.boundHashOps("CAT:" + cat + ":" + id).get("CN");
            if (cn != null && Integer.valueOf(cn.toString()) > 0) {
                //3)判断该商品是否有库存
                //4)如果有库存，则减库存，同时下单操作
                String pc = redisTemplate.boundHashOps("CAT:" + cat + ":" + id).get("PC").toString();
                Order order = new Order();
                order.setTotalNum(1);
                order.setPayStatus("1");
                order.setCreateTime(TimeUtil.getNextMonth(new Date()));
                order.setCatId(Integer.valueOf(cat));
                order.setUpdateTime(order.getCreateTime());
                order.setOrderStatus("0");
                order.setPayStatus("0");
                order.setIsDelete("0");
                order.setSkuId(id);
                order.setPrice(Integer.valueOf(pc));
                order.setUsername(username);
                orderMapper.insert(order);
                //则减库存
                redisTemplate.boundHashOps("CAT:" + cat + ":" + id).increment("CN", -1);

                //抢单成功
                String key = "CAT:" + cat + "-" + id + "-" + username;
                redisTemplate.boundValueOps(key).set(order.getId(), 120, TimeUnit.SECONDS);
            }
        } finally {
            //没有抽取
            //redisTemplate.delete(lockKey);
            //lock.unlock();
            this.redisLock.unLock(lockKey);
        }
    }
}
